#include "Log.hpp"
#include <string>
#include <sys/types.h>          
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>


typedef function<std::string(const std::string&)> func_t;

const int size = 1024;
std::string defaultip = "0.0.0.0";
uint16_t defaultport = 8080;

Log log;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR
};

class UdpServer
{
public:
    UdpServer(const uint16_t& port = defaultport)
        : _sockfd(0)
        , _ip(defaultip)
        , _port(port)
    {}

    void Init()
    {
        // 创建udp socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            log(Fatal, "socket create error, errno: %d, err string: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        log(Info, "socket create success, sockfd: %d", _sockfd);

        // bind socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = inet_addr(_ip.c_str());
        // local.sin_addr.s_addr = htonl(INADDR_ANY);

        if(bind(_sockfd, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            log(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));
    }

    void CheckUser(const struct sockaddr_in client, const std::string& clientip, uint16_t clientport)
    {
        auto it = _online_user.find(clientip);
        if(it == _online_user.end())
        {
            _online_user.insert(make_pair(clientip, client));
            std::cout << "[" << clientip << ":" << clientport << "] add to online user." << std::endl;
        }
    }

    void BoardCast(const std::string& info, const std::string clientip, uint16_t clientport)
    {
        for(auto& user : _online_user)
        {
            std::string message = "[";
            message += clientip;
            message += ": ";
            message += std::to_string(clientport);
            message += "]# ";
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, (const struct sockaddr*)(&user.second), len);
        }
    }

    void Run()
    {
        char inbuffer[size];
        
        while(true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if(n < 0)
            {
                log(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;
            }
            
            uint16_t clientport = ntohs(client.sin_port);
            std::string clientip = inet_ntoa(client.sin_addr);
            CheckUser(client, clientip, clientport);

            std::string info = inbuffer;
            BoardCast(info, clientip, clientport);
        }
    }

    ~UdpServer()
    {}

private:
    int _sockfd;            // 网络文件描述符
    std::string _ip;        // 任意地址 bind 0
    uint16_t _port;         // 表明服务器进程的端口号
    unordered_map<std::string, struct sockaddr_in> _online_user;
};


